package cz.muni.fi.pa165.sportclubmanager.client.modules.player;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.factories.ButtonBarFactory;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.validation.ValidationResult;
import com.jgoodies.validation.ValidationResultModel;
import com.jgoodies.validation.util.DefaultValidationResultModel;
import com.jgoodies.validation.util.ValidationUtils;
import com.jgoodies.validation.view.ValidationComponentUtils;
import com.jgoodies.validation.view.ValidationResultViewFactory;
import cz.muni.fi.pa165.sportclubmanager.client.Main;
import cz.muni.fi.pa165.sportclubmanager.service.player.PlayerDTO;
import cz.muni.fi.pa165.sportclubmanager.service.player.PlayerService;
import cz.muni.fi.pa165.sportclubmanager.service.player.PlayerWebService;
import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.swing.*;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;

/**
 * Form for editing Age category with validation
 * 
 * @author mprokes
 */
public final class PlayerForm extends JPanel {
        
    private PlayerDTO player;
    private JComponent parent;

    private final JFrame frame = new JFrame("Create new player");
    private final JButton submitButton = new JButton(new OkAction());
    private final JButton cancelButton = new JButton(new CancelAction());    
    private final JLabel hintLabel = new JLabel();
    private final JTextField firstNameInput = new JTextField(30);
    private final JTextField lastNameInput = new JTextField(30);
    private final JTextField birthDateInput = new JTextField(30);
    private final JTextField weightInput = new JTextField(30);
    private final JTextField heightInput = new JTextField(30);
    private JProgressBar progressBar;
    private final ValidationResultModel validationResultModel =
            new DefaultValidationResultModel();

    /**
     * Main constructor
     * @param parent parent component
     */
    public PlayerForm(JComponent parent) {               
        //set texts
        this.frame.setTitle("Create new age category");
        submitButton.setText("Add");

        //mandatory fields        
        ValidationComponentUtils.setMandatory(this.firstNameInput, true);
        ValidationComponentUtils.setMandatory(this.lastNameInput, true);
        ValidationComponentUtils.setMandatory(this.birthDateInput, true);
        ValidationComponentUtils.setMandatory(this.weightInput, true);
        ValidationComponentUtils.setMandatory(this.heightInput, true);

        //create a hint for each of the three validated fields
        ValidationComponentUtils.setInputHint(firstNameInput, "Players first name.");
        ValidationComponentUtils.setInputHint(lastNameInput, "Players last name.");
        ValidationComponentUtils.setInputHint(birthDateInput, "Birth date in format dd.mm.yyyy or yyy.mm.dd");
        ValidationComponentUtils.setInputHint(weightInput, "Weight of player");
        ValidationComponentUtils.setInputHint(heightInput, "Height of player");
        //update the hint based on which field has focus
        KeyboardFocusManager.getCurrentKeyboardFocusManager()
                .addPropertyChangeListener(new FocusChangeHandler());

        this.parent = parent;
        validateForm();

        frame.add(createPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(Main.getMainWindow());
        frame.setVisible(true);
    }

    /**
     * Constructor for editing existing category
     * 
     * @param ageCategory existing ageCategory
     * @param parent parent component
     */
    PlayerForm(PlayerDTO player, JComponent parent) {
        this(parent);
        this.player = player;
        
        //change texts according to situation
        frame.setTitle("Edit player");
        submitButton.setText("Save");

        //fill input boxes with values from ws
        SimpleDateFormat df = new SimpleDateFormat();
        df.applyPattern("yyyy.MM.dd");
        
        firstNameInput.setText(player.getFirstName());      
        lastNameInput.setText(player.getLastName());
        birthDateInput.setText(df.format(
                    player.getBirthDate().toGregorianCalendar().getTime()).toString());
        weightInput.setText(Integer.toString(player.getWeight()));
        heightInput.setText(Integer.toString(player.getHeight()));
    }

    /**
     * Create dialog panel
     * @return 
     */
    private JPanel createPanel() {
        FormLayout layout = new FormLayout("pref, 2dlu, pref:grow");
        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        int columnCount = builder.getColumnCount();
        builder.setDefaultDialogBorder();

        //add the label that will show validation hints, with an icon
        hintLabel.setIcon(ValidationResultViewFactory.getInfoIcon());
        builder.append(this.hintLabel, columnCount);

        //add form inputs        
        builder.append("First name", this.firstNameInput);        
        builder.append("Last name", this.lastNameInput);        
        builder.append("Birth date", this.birthDateInput);        
        builder.append("Weight", this.weightInput);        
        builder.append("Height", this.heightInput);        

        ValidationComponentUtils.updateComponentTreeMandatoryBackground(
                builder.getPanel());

        //add a component to show validation messages
        JComponent validationResultsComponent =
                ValidationResultViewFactory.createReportList(
                this.validationResultModel);
        builder.appendUnrelatedComponentsGapRow();
        builder.appendRow("fill:50dlu:grow");
        builder.nextLine(2);
        builder.append(validationResultsComponent, columnCount);

        //progress bar for showing remote action in progress
        progressBar = new JProgressBar();
        builder.append(progressBar, columnCount);
        
        //button bar (OK,Cancel)
        JPanel buttonBar = ButtonBarFactory.buildOKCancelBar(
                submitButton, new JButton(new CancelAction()));
        builder.append(buttonBar, columnCount);       

        return builder.getPanel();
    }
    
    
    /**
     * Validat form input
     * @return 
     */
    private ValidationResult validateForm() {
        ValidationResult validationResult = new ValidationResult();

        // validate name field
        if (this.firstNameInput.getText().isEmpty()) {
            validationResult.addError("The First name field can not be blank.");
        } else if (!ValidationUtils.hasBoundedLength(
                this.firstNameInput.getText(), 1, 255)) {
            validationResult.addError(
                    "The First name field must be between 1 and 255 characters.");
        }

         // validate name field
        if (this.lastNameInput.getText().isEmpty()) {
            validationResult.addError("The Last name field can not be blank.");
        } else if (!ValidationUtils.hasBoundedLength(
                this.lastNameInput.getText(), 1, 255)) {
            validationResult.addError(
                    "The Last name field must be between 1 and 255 characters.");
        }
        
        
         // validate height field
        if (this.heightInput.getText().isEmpty()) {
            validationResult.addError("The Height field can not be blank.");
        } else if (!ValidationUtils.isNumeric(
                this.heightInput.getText())) {
            validationResult.addError(
                    "The Height field must be a number.");
        }
        
        // validate weight field
        if (this.weightInput.getText().isEmpty()) {
            validationResult.addError("The Weight field can not be blank.");
        } else if (!ValidationUtils.isNumeric(
                this.weightInput.getText())) {
            validationResult.addError(
                    "The Weight field must be a number.");
        }
        
         // validate date field
        if (birthDateInput.getText().isEmpty()) {
            validationResult.addError("The Birth date field can not be blank.");
        }
        else {
            
            if(!birthDateInput.getText().matches("^([0-9]{2}\\.[0-9]{2}\\.[0-9]{4}|[0-9]{4}\\.[0-9]{2}\\.[0-9]{2})$")){
                validationResult.addError(
                    "The birth date is not correct.");
            }
        }

        

        return validationResult;
    }

    /**
     * Worker for submit form
     */
    public class SubmitFormWorker extends SwingWorker<Integer, Integer> {

        ValidationResult validationResult = new ValidationResult();

        @Override
        protected Integer doInBackground() throws Exception {            
            PlayerService playerService = new PlayerWebService().getPlayerServiceImplPort();
            
            try {
                if (player.getId() == null || player.getId() < 1) {
                    playerService.create(player);
                } else {
                    playerService.update(player);
                }


                return 0;
            } catch (Exception e) {
                validationResult.addError("Error: " + e.getMessage());
                return 1;
            }
        }

        @Override
        protected void done() {
            super.done();

            //print messages
            validationResultModel.setResult(validationResult);
            
            //close dialog if no errors
            if (!validationResult.hasErrors()) {
                ((PlayerListPanel) parent).reloadPlayersTable();
                frame.dispose();
            }

            //buttons
            submitButton.setEnabled(true);
            cancelButton.setEnabled(true);
            progressBar.setIndeterminate(true);
            progressBar.setVisible(false);
        }
    }

    /**
     * OK button action
     */
    private final class OkAction extends AbstractAction {

        private OkAction() {
            super("OK");
        }

        public void actionPerformed(ActionEvent e) {
            //don't close the frame on OK unless it validates                            
            ValidationResult validationResult = validateForm();
            validationResultModel.setResult(validationResult);

            //submit form if it is valid
            if (!validationResultModel.hasErrors()) {
                if (player == null) {
                    player = new PlayerDTO();
                }


                player.setFirstName(firstNameInput.getText());
                player.setLastName(lastNameInput.getText());
                player.setHeight(Integer.parseInt(heightInput.getText()));
                player.setWeight(Integer.parseInt(weightInput.getText()));
                Date date = new Date();
                try {
                    if (birthDateInput.getText().charAt(4) == '.') {
                        date = new SimpleDateFormat("yyyy.MM.dd").parse(birthDateInput.getText());
                    } else {
                        date = new SimpleDateFormat("dd.MM.yyyy").parse(birthDateInput.getText());
                    }
                } catch (ParseException ex) {
                }
                GregorianCalendar c = new GregorianCalendar();
                c.setTime(date);
                try {
                    player.setBirthDate(DatatypeFactory.newInstance().newXMLGregorianCalendar(c));
                } catch (DatatypeConfigurationException ex) {
                }

                SubmitFormWorker worker = new SubmitFormWorker();
                submitButton.setEnabled(false);
                //cancelButton.setEnabled(false);
                progressBar.setIndeterminate(true);
                progressBar.setVisible(true);
                worker.execute();
            }
        }
    }
    /**
     * Cancel button action
     */
    private final class CancelAction extends AbstractAction {

        private CancelAction() {
            super("Cancel");
        }

        public void actionPerformed(ActionEvent e) {
            frame.dispose();
        }
    }

    /**
     * Update label hint based on focus
     */
    private final class FocusChangeHandler
            implements PropertyChangeListener {

        public void propertyChange(PropertyChangeEvent evt) {
            String propertyName = evt.getPropertyName();
            if ("permanentFocusOwner".equals(propertyName)) {
                Component focusOwner = KeyboardFocusManager
                        .getCurrentKeyboardFocusManager().getFocusOwner();

                if (focusOwner instanceof JTextField) {
                    JTextField field = (JTextField) focusOwner;
                    String focusHint = (String) ValidationComponentUtils
                            .getInputHint(field);
                    hintLabel.setText(focusHint);
                } else {
                    hintLabel.setText("");
                }

            }
        }
    }
}
